define([], function() {
  const line = '\n';
  const rempty = /^\s*$/;

  function padIndent(index, indent) {
    var indentChars = '';
    for (var i = 0; i < index; i++) {
      indentChars += indent;
    }
    return indentChars;
  }

  function newLine(draws, index, indent, options) {
    options = options || {};
    var current = options.current;
    var newline = true, removed = false;
    if (current) {
      var beforeChar = '';
      switch(current) {
        case '}':
          beforeChar = '{';
          break;
        case ']':
          beforeChar = '[';
          break;
        case '/':
          beforeChar = '/';
        default:
          break;
      }
      if (beforeChar == '/') {
        for (var i = options.index - 1; i >= 0; i--) {
          var char = options.string.charAt(i);
          if (char == '\n') {
            break;
          } else if (char != ' ' && char != '\t') {
            removed = true;
            newline = false;
            for (var j = draws.length - 1; j >= 0; j--) {
              if (rempty.test(draws[j])) {
                draws.length = draws.length - 1;
              } else {
                break;
              }
            }
            break;
          } 
        }
      } else if (draws.length >= 3) {
          removed = true;
          if (draws[draws.length - 3] == beforeChar) {
          if (draws[draws.length - 2] == line) {
            if (rempty.test(draws[draws.length - 1])) {
              newline = false;
              draws.length = draws.length - 2;
            }
          }
        } else if (draws[draws.length - 2] == line) {
          if (rempty.test(draws[draws.length - 1])) {
            newline = false;
            draws[draws.length - 1] = padIndent(index, indent);
          }
        }
      }
    }
    if (!removed && draws.length >= 2) {
      if (draws[draws.length - 2] == line) {
        if (rempty.test(draws[draws.length - 1])) {
          newline = false;
        }
      }
    }
    if (newline) {
      draws.push(line);
      draws.push(padIndent(index, indent));
    }
  }

  function formatString(string, indent, compress) {
    if (rempty.test(string)) {
      return '';
    }
    try {
      indent = indent || '    ';
      compress = compress || false;

      var draws = [];
      var index = 0;
      var quoteCount = 0, squoteCount = 0;
      var quoteOddEven = 0, squoteOddEven = 0;
      var commentOddEven = 0;
      var current = '';
      var prevChar = '';
      var nextChar = '';
      for (var i = 0; i < string.length; i++) {
        current = string.charAt(i);
        if (i + 1 < string.length) {
          nextChar = string.charAt(i + 1);
        } else {
          nextChar = '';
        }
        switch (current) {
        case '{':
        case '[':
          if (commentOddEven == 0) {
            if (prevChar == '"' || prevChar == '\'') {
              draws.push(current);
            } else if (quoteOddEven == 0 && squoteOddEven == 0) {
              draws.push(current);
              if (!compress) {
                index++;
                newLine(draws, index, indent);
              }
            } else {
              draws.push(current);
            }
          } else {
            draws.push(current);
          }
          break;
        case '/':
          if (quoteOddEven == 0 && squoteOddEven == 0) {
            if (nextChar == '*') {
              commentOddEven = 1;
              draws.push(current);
            } else if (prevChar == '*') {
              commentOddEven = 0;
              draws.push(current);
              if (!compress) {
                newLine(draws, index, indent);
              }
            } else if (nextChar == '/' && commentOddEven == 0) {
              if (!compress) {
                newLine(draws, index, indent, {
                  current: current,
                  string: string,
                  index: i
                });
              }
              for (var j = i; j < string.length; j++) {
                current = string.charAt(j);
                draws.push(current);
                i = j;
                if (current == line) {
                  if (!compress) {
                    draws.push(padIndent(index, indent));
                  }
                  break;
                }
              }
            } else {
              draws.push(current);
            }
          } else {
            draws.push(current);
          }
          break;
        case '}':
        case ']':
          if (commentOddEven == 0) {
            if (nextChar == '"' || nextChar == '\'') {
              draws.push(current);
            } else if (quoteOddEven == 0 && squoteOddEven == 0) {
              if (!compress) {
                index--;
                newLine(draws, index, indent, {
                  current: current
                });
              }
              draws.push(current);
            } else {
              draws.push(current);
            }
          } else {
            draws.push(current);
          }
          break;
        case ',':
          if (commentOddEven == 0) {
            if (quoteOddEven == 0 && squoteOddEven == 0) {
              draws.push(current);
              if (!compress) {
                newLine(draws, index, indent);
              }
            } else {
              draws.push(current);
            }
          } else {
            draws.push(current);
          }
          break;
        case '\\':
          if (commentOddEven == 0) {
            draws.push(current);
            if ((quoteOddEven != 0 && nextChar == '"') || (squoteOddEven != 0 && nextChar == '\'')) {
              current = nextChar;
              draws.push(current);
              i++;
            }
          } else {
            draws.push(current);
          }
          break;
        case ' ':
        case '\t':
        case '\r':
        case '\n':
          if (commentOddEven != 0 || quoteOddEven != 0 || squoteOddEven != 0) {
            draws.push(current);
          }
          break;
        case ':':
          if (commentOddEven == 0) {
            draws.push(current);
            if (quoteOddEven == 0 && squoteOddEven == 0) {
              if (!compress) {
                draws.push(' ');
              }
            }
          } else {
            draws.push(current);
          }
          break;
        case '\'':
          if (commentOddEven == 0 && quoteOddEven == 0) {
            squoteCount = squoteCount + 1;
            squoteOddEven = (squoteOddEven + 1) % 2;
            draws.push(current);
          } else {
            draws.push(current);
          }
          break;
        case '"':
          if (commentOddEven == 0 && squoteOddEven == 0) {
            quoteCount = quoteCount + 1;
            quoteOddEven = (quoteOddEven + 1) % 2;
            draws.push(current);
          } else {
            draws.push(current);
          }
          break;
        default:
          draws.push(current);
        }
        prevChar = current;
      }

      return draws.join('');
    } catch (e) {
      console.error(e);
      return string;
    }
  }
  return {
    format: formatString,
    minify: function(string) {
      return formatString(string, '', true);
    }
  }
});
